package input

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"m3u8/config"
	"m3u8/info"
	"m3u8/util"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/quangngotan95/go-m3u8/m3u8"
)

const Symbol string = "#"

type Simple struct {
	url          string
	count        int
	files        []string
	proxy        string
	Items        []*m3u8.SegmentItem
	Domain       string
	SchemaDomain string
	index        int
	dir          string
}

func (i *Simple) RemoveFile() {
	if err := os.RemoveAll(i.saveDir()); err != nil {
		fmt.Println("removing useless files error", err.Error()+"\n")
	}
}

func (i *Simple) GetUrl() string {
	return i.url
}

func (i *Simple) GetCount() int {
	return i.count
}

func (i *Simple) GetFiles() []string {
	return i.files
}

func (i *Simple) Dir() string {
	return config.GetConfig().Base.SaveRoot
}

func (i *Simple) saveDir() string {
	if i.dir == "" {
		panic("empty dir")
	}
	return config.GetConfig().Base.SaveRoot + i.dir
}

func (i *Simple) MaxConnection() int {
	return config.GetConfig().Base.Count
}

func (i *Simple) GetM3u8Name() string {
	nowDate := time.Now()
	month := int(nowDate.Month())
	day := nowDate.Day()
	hour := nowDate.Hour()
	minute := nowDate.Minute()
	second := nowDate.Second()
	monthStr := strconv.Itoa(month)
	dayStr := strconv.Itoa(day)
	hourStr := strconv.Itoa(hour)
	minuteStr := strconv.Itoa(minute)
	secondStr := strconv.Itoa(second)
	if month < 10 {
		monthStr = "0" + monthStr
	}
	if day < 10 {
		dayStr = "0" + dayStr
	}
	if hour < 10 {
		hourStr = "0" + hourStr
	}
	if minute < 10 {
		minuteStr = "0" + minuteStr
	}
	if second < 10 {
		secondStr = "0" + secondStr
	}
	return strconv.Itoa(nowDate.Year()) +
		monthStr +
		dayStr +
		hourStr +
		minuteStr +
		secondStr
}

func (i *Simple) GetTsNamePattern() string {
	panic("简单模式不支持此方法,请使用Input struct")
}

func (i *Simple) Check() {
	for _, filename := range i.GetFiles() {
		if !util.Exists(filename) {
			fmt.Println("文件不全")
			os.Exit(-1)
		}
	}
}

func (i *Simple) GetIndex() int {
	return i.index
}

func (i *Simple) Parse() {
	i.url = os.Args[1]
	i.proxy = config.GetConfig().Base.AgentHost
	transport := &http.Transport{DisableKeepAlives: true}
	if i.proxy != "" {
		proxy := func(_ *http.Request) (*url.URL, error) {
			return url.Parse(i.proxy) //根据定义Proxy func(*Request) (*url.URL, error)这里要返回url.URL
		}
		transport.Proxy = proxy
	}

	client := &http.Client{Transport: transport, Timeout: time.Second * 60 * 10}
	request, err1 := http.NewRequest("GET", i.url, nil)
	if err1 != nil {
		fmt.Println(err1)
		os.Exit(1)
	}

	/*resp,_:=http.Get("https://cp2.hboav.com/check/hbo8/hls/files/mp4/u/D/7/uD7bd.mp4/index.m3u8?key=UeZ4Kl35J1KhB_600tmjVA&expires=1626431677")
	respB,_:=ioutil.ReadAll(resp.Body)
	fmt.Println(string(respB))*/

	response, err2 := client.Do(request)
	if err2 != nil {
		fmt.Println(err2)
		os.Exit(2)
	}
	playlist, err3 := m3u8.Read(response.Body)
	if err3 != nil {
		fmt.Println(err3)
		os.Exit(3)
	}
	i.Items = playlist.SegmentItems()
	i.count = len(i.Items)
	i.files = make([]string, i.count)

	split := strings.Split(i.url, "/")
	path := split[0 : len(split)-1]
	i.Domain = strings.Join(path, "/") + "/"

	urlParse, _ := url.Parse(i.url)
	i.SchemaDomain = urlParse.Scheme + "://" + urlParse.Hostname() + "/"

	i.createDir(playlist.String())
}

func (i *Simple) createDir(str string) {

	md := md5.New()
	md.Write([]byte(str))
	mdStr := md.Sum(nil)

	i.dir = hex.EncodeToString(mdStr) /*strconv.Itoa(rand.Intn(1000))*/ + "/"

	err := os.MkdirAll(i.Dir()+i.dir, os.ModePerm)
	if err != nil {
		fmt.Print("创建文件夹失败", err.Error())
		os.Exit(1)
	}
}

func (i *Simple) SaveTs(idx int, channel chan info.Info, downChan chan bool, wg *sync.WaitGroup) {
	//进队列
	downChan <- true

	index := idx - 1

	defer func() {
		wg.Done()
		<-downChan
	}()

	// 下面这个seg是从m3u8中获取的一个片段的URL
	// 注意，这个URL有三种可能的形式，完整路径、不带协议和主机的绝对路径、相对路径
	seg := i.Items[index].Segment
	// filename是从网络上下载的完整路径，如果没有协议头和主机信息，得给他加上
	filename := seg
	// 判断片段是以上三种形式的哪种
	parseurl, _ := url.Parse(filename)
	if parseurl.Scheme == "" {
		segByte := []byte(seg)
		if segByte[0] == '/' {
			filename = i.SchemaDomain + filename
		} else {
			filename = i.Domain + filename
		}
	}
	// 不管是上面哪种形式，都需要去掉目录部分，要不然创建文件就失败了
	split := strings.Split(seg, "/")
	seg = split[len(split)-1]
	// 本地文件的完整文件名
	saveFilename := i.saveDir() + seg
	if util.Exists(saveFilename) {
		i.files[index] = saveFilename
		channel <- info.Info{Num: idx, Msg: seg + "已存在"}
		return
	}

	transport := &http.Transport{DisableKeepAlives: true}
	if i.proxy != "" {
		proxy := func(_ *http.Request) (*url.URL, error) {
			return url.Parse(i.proxy) //根据定义Proxy func(*Request) (*url.URL, error)这里要返回url.URL
		}
		transport.Proxy = proxy
	}

	for tryCount := 1; tryCount <= 3; tryCount++ {
		now := time.Now()
		client := &http.Client{Transport: transport, Timeout: time.Second * 30}
		request, err1 := http.NewRequest("GET", filename, nil)
		if err1 != nil {
			channel <- info.Info{IsTry: true, Num: idx, Msg: fmt.Sprintf("请求出错:%s,第%d次重试", err1.Error(), tryCount)}
			continue
		}
		request.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36")
		request.Header.Add("Accept-Encoding", "identity")
		response, err2 := client.Do(request)
		if err2 != nil {
			channel <- info.Info{IsTry: true, Num: idx, Msg: fmt.Sprintf("响应出错:%s,重,第%d次重试", err2.Error(), tryCount)}
			continue
		}
		defer response.Body.Close()

		saveFile, err := os.Create(saveFilename)
		if err != nil {
			channel <- info.Info{IsTry: true, Num: idx, Msg: fmt.Sprintf("创建文件失败:%s,第%d次重试", err.Error(), tryCount)}
			continue
		}
		defer saveFile.Close()
		wInt, err := io.Copy(saveFile, response.Body)
		if err != nil && err != io.ErrUnexpectedEOF {
			channel <- info.Info{IsTry: true, Num: idx, Msg: fmt.Sprintf("写入文件失败:%s,第%d次重试", err.Error(), tryCount)}
			continue
		}

		i.files[index] = saveFilename
		channel <- info.Info{Num: idx, Msg: seg + util.FileSize(wInt) + " 用时：" + util.HumanTime(now)}
		break
	}
}
